package org.gdstash.db;

import org.gdstash.file.ARZRecord;
import org.gdstash.util.GDMsgFormatter;
import org.gdstash.util.GDMsgLogger;

import java.sql.*;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class DBLootTableSet {
    private static final String TABLE_NAME = "GD_LOOTTABLESET_HD";
    private static final int ROW_TABLESET_ID = 1;
    private static ConcurrentHashMap<String, DBLootTableSet> hashBuffer;
    private String tableSetID;
    private List<DBLootTableSetAlloc> entries;

    public DBLootTableSet() {
        this.entries = new LinkedList<DBLootTableSetAlloc>();
    }

    private DBLootTableSet(final ARZRecord record) {
        this.tableSetID = record.getFileName();
        this.entries = record.getTableSetAllocList();
    }

    static {
        DBLootTableSet.hashBuffer = new ConcurrentHashMap<String, DBLootTableSet>();
    }

    public String getTableSetID() {
        return this.tableSetID;
    }

    public static void clearBuffer() {
        DBLootTableSet.hashBuffer.clear();
    }

    public static void createTables() throws SQLException {
        final String dropTable = "DROP TABLE GD_LOOTTABLESET_HD";
        final String createTable = "CREATE TABLE GD_LOOTTABLESET_HD (TABLESET_ID VARCHAR(256) NOT NULL, PRIMARY KEY (TABLESET_ID))";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final Statement st = conn.createStatement()) {
                if (GDDBUtil.tableExists(conn, "GD_LOOTTABLESET_HD")) {
                    st.execute(dropTable);
                }
                st.execute(createTable);
                st.close();
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args = {"GD_LOOTTABLESET_HD"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_CREATE_TABLE", args);
                GDMsgLogger.addError(msg);
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        }
        DBLootTableSetAlloc.createTable();
    }

    public static void delete(final String tableSetID) throws SQLException {
        final String deleteEntry = "DELETE FROM GD_LOOTTABLESET_HD WHERE TABLESET_ID = ?";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final PreparedStatement ps = conn.prepareStatement(deleteEntry)) {
                ps.setString(1, tableSetID);
                ps.executeUpdate();
                ps.close();
                DBLootTableSetAlloc.delete(conn, tableSetID);
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args = {tableSetID, "GD_LOOTTABLESET_HD"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_DEL_TABLE_BY_ID", args);
                GDMsgLogger.addError(msg);
                GDMsgLogger.addError(ex);
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        } catch (SQLException ex2) {
            throw ex2;
        }
    }

    public static void insert(final ARZRecord record) throws SQLException {
        final DBLootTableSet entry = get(record.getFileName());
        if (entry != null) {
            return;
        }
        final DBLootTableSet tableSet = new DBLootTableSet(record);
        if (tableSet.entries == null) {
            return;
        }
        if (tableSet.entries.isEmpty()) {
            return;
        }
        final String insert = "INSERT INTO GD_LOOTTABLESET_HD VALUES (?)";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final PreparedStatement ps = conn.prepareStatement(insert)) {
                ps.setString(1, record.getFileName());
                ps.executeUpdate();
                ps.close();
                conn.commit();
                DBLootTableSetAlloc.insert(conn, tableSet);
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args = {record.getFileName(), "GD_LOOTTABLESET_HD"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_INS_TABLE_BY_ID", args);
                GDMsgLogger.addWarning(msg);
                GDMsgLogger.addWarning(ex);
            } finally {
                conn.setAutoCommit(auto);
            }
        }
    }

    public static DBLootTableSet get(final String tableSetID) {
        DBLootTableSet set = null;
        set = DBLootTableSet.hashBuffer.get(tableSetID);
        if (set == null) {
            set = getDB(tableSetID);
            if (set != null) {
                DBLootTableSet.hashBuffer.put(set.tableSetID, set);
            }
        }
        return set;
    }

    private static DBLootTableSet getDB(final String tableSetID) {
        DBLootTableSet set = null;
        final String command = "SELECT * FROM GD_LOOTTABLESET_HD WHERE TABLESET_ID = ?";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, tableSetID);
            try (final ResultSet rs = ps.executeQuery()) {
                final List<DBLootTableSet> list = wrap(rs);
                if (list.isEmpty()) {
                    set = null;
                } else {
                    set = list.get(0);
                }
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {tableSetID, "GD_LOOTTABLESET_HD"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return set;
    }

    public static List<DBLootTableSet> getByTableID(final String tableID) {
        List<DBLootTableSet> list = null;
        final String command = "SELECT * FROM GD_LOOTTABLESET_HD WHERE TABLE_ID = ?";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, tableID);
            try (final ResultSet rs = ps.executeQuery()) {
                list = wrap(rs);
                conn.commit();
            } catch (SQLException ex) {
                list = null;
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {tableID, "GD_LOOTTABLESET_HD"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    private static List<DBLootTableSet> wrap(final ResultSet rs) throws SQLException {
        final List<DBLootTableSet> list = new LinkedList<DBLootTableSet>();
        while (rs.next()) {
            final DBLootTableSet set = new DBLootTableSet();
            set.tableSetID = rs.getString(1);
            set.entries = DBLootTableSetAlloc.getByTableSetID(set.tableSetID);
            list.add(set);
        }
        return list;
    }

    public List<DBLootTableSetAlloc> getAllocList() {
        return this.entries;
    }
}
